﻿namespace LiSAClient.ServiceProvider
{
    using System;
    using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using log4net;

    public class ServiceHandler : IServiceExceptionsHandler
    {

        public static ServiceHandler Instance { get; private set; }

        private ClientCredentials credentials;

        private ILog log;

        static ServiceHandler()
        {
            Instance = new ServiceHandler();
        }

        private ServiceHandler()
        {
            this.log = LogManager.GetLogger(typeof(ServiceHandler));
            this.RegisterExceptionsHandler(this);
        }

        public static bool OnValidationCallback(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }

        #region Exceptions handling
        private IServiceExceptionsHandler exceptionsHandler;

        public void RegisterExceptionsHandler(IServiceExceptionsHandler handler)
        {
            this.exceptionsHandler = handler;
        }
        #endregion

        public ServiceHandler SetCredentials(string user, string pass)
        {
            this.credentials = new ClientCredentials();
            this.credentials.UserName.UserName = user;
            this.credentials.UserName.Password = pass;

            return this;
        }


        public void WithService(Action<LiSAService.ServiceClient> action)
        {
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnValidationCallback);
            ServicePointManager.Expect100Continue = false;
            using (var client = new LiSAService.ServiceClient())
            {
                client.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
                client.ChannelFactory.Endpoint.Behaviors.Add(this.credentials);

                try
                {
                    action(client);
                }
                catch (Exception exc)
                {
                    log.Error(string.Format("Error occurred calling a query or command: \n {0} \n {1}",exc.GetBaseException(), exc.InnerException));
                }
                finally
                {
                    if (client.State == CommunicationState.Faulted)
                    {
                        client.Abort();
                    }

                    client.Close();
                }
            }
        }

        public T WithService<T>(Func<LiSAService.ServiceClient, T> action)
        {
            T result = default(T);

            this.WithService(client => { result = action(client); });

            return result;
        }


        /// <summary>
        /// Default way to handle exceptions, just logs and consumes the error.
        /// There should be registered another IServiceExceptionsHandler, for showing appropiate GUI etc. 
        /// </summary>
        /// <typeparam name="T">A type of error.</typeparam>
        /// <param name="e">The error.</param>
        public void Handle<T>(T e) where T : Exception
        {
            this.log.Error(e.Message, e);
        }
    }
}
